
-- 用于记录 AnimState 到 Entity 的映射
-- local map = {}

-- AddPrefabPostInitAny(function (inst)
--     local anim = inst.AnimState
--     if anim then
--         map[anim] = inst
--     end
-- end)

-- local old_PlayAnimation =  GLOBAL.AnimState.PlayAnimation
-- function GLOBAL.AnimState:PlayAnimation(animname,loop,...)
--     local inst = map[self]
--     if inst and inst.prefab == 'webbert' then
--         print("播放动画:", animname)
--     end
--     return old_PlayAnimation(self, animname,loop,...)
-- end


-- local map = {
--     clocktick = true,
--     locomote = true,
--     sanitydelta = true,
--     moisturedelta = true,
--     temperaturedelta = true,
--     weathertick = true,
--     overridecolourmodifier = true,
--     pre_health_setval = true,
--     healthdelta = true,
--     entitysleep = true,
--     entity_spawned = true,
--     entitywake = true,
--     nightmareclocktick = true,
--     temperaturetick = true,
--     actioncomponentsdirty= true,
--     on_landed = true,
--     nightmarephasechanged = true,
--     animover = true,
--     timerdone = true,
--     sanitydirty = true,
--     lightdirty = true,
--     hungerdelta = true,
--     onreachdestination = true,
--     newstate = true,
--     animqueueover = true,
--     pulsetimedirty = true,
--     master_clockupdate = true,
--     onremove = true,
--     healthdirty = true,
--     perishdirty = true,
--     forceperishchange = true,
--     isperformactionsuccessdirty = true,
--     performaction = true,
--     temperaturedirty = true,
--     mouseover = true,
--     mouseout = true,
--     clientsideinventoryflagschanged = true,
--     invincibletoggle = true,
--     windchange = true,
--     colourtweener_end = true,
--     skilltreeinitialized_client = true,
--     ms_skilltreeinitialized = true,
--     onsetskillselection_server = true,
--     colourtweener_start = true,
--     moisturedirty = true,
--     playervotechanged = true,
--     fx_spawned = true,
--     hideworldreset = true,
--     floater_startfloating = true,
--     landeddirty = true,
--     ms_registersoildrinker = true,
--     ms_moonstormwindowover = true,
--     plantherdspawned = true,
--     playerentered = true,
--     playercountsdirty = true,
--     yotd_ratraceprizechange = true,
--     onwordpredictionupdated = true,
--     perishchange = true,
--     donetalking = true,
--     sharksounddirty = true,
--     hungerdirty = true,
--     chatterdirty = true,
--     ontalk = true,
--     techtreechange = true,
--     current_prototyper_dirty = true,
--     itemlose = true,
--     exitlimbo = true,
--     forgetinventoryitem = true,
--     unregister_hudindicatable = true,
--     moonphasestylechanged = true,
--     mounthurt = true,
--     yotbskinupdate = true,
--     item_buff_changed = true,
--     roseglassescooldownchanged = true,
--     inspectaclesgamechanged = true,
--     isacidsizzling = true,
--     miasmalevel = true,
--     stagedirty = true,
--     techtreesdirty = true,
--     ms_spawnedsharkboiarena = true,
--     insight_moisture_rate_dirty = true,
--     imagechange = true,
--     on_no_longer_landed = true,
--     serverpauseddirty = true,
--     refreshhudsize = true,
--     ms_playercounts = true,
-- }
-- local old_PushEvent = EntityScript.PushEvent
-- function EntityScript:PushEvent(eventname, ...)
--     if not map[eventname] then
--         print(eventname)
--     end
--     return old_PushEvent(self, eventname, ...)
-- end



-- AddStategraphPostInit('wilson',function (sg)
--     for _,v in ipairs(sg.states.doshortaction.timeline) do
--         if (v.time == 8 * FRAMES) then
--             local old_fn = v.fn
--             v.fn = function (inst)
--                 local buffaction = inst:GetBufferedAction()
--                 local target = buffaction ~= nil and buffaction.target or nil
--                 old_fn(inst)
--                 print(target)
--                 if target == nil or not target:IsValid() or target:HasTag("INLIMBO") then
--                     print('goto idle')
--                     inst.sg:GoToState("idle", true)
--                 end
--             end
--         end
--     end
-- end)


-- local old_PerformBufferedAction = EntityScript.PerformBufferedAction
-- function EntityScript:PerformBufferedAction()
--     if self.components and self.components.talker then
--         self.components.talker:Say('我做不到!')
--     end
-- end



--[[

-- 按H键生成跟随指针的闪电

local namespace = 'qy'
local blank_prefab = 'fx_webbert_misc' -- 这里填个空prefab

-- local function fn()
--     local inst = CreateEntity()
--     inst.entity:AddTransform()
--     inst.entity:AddAnimState()
--     inst.entity:AddNetwork()
--     inst.entity:AddSoundEmitter()
--     inst:AddTag("NOCLICK")
--     inst:AddTag("NOBLOCK")
--     inst:AddTag("FX")
--     if not TheWorld.ismastersim then return inst end
--     inst.persists = false
--     return inst
-- end
-- return Prefab(prefab_id, fn)



local tool = {}

---做周期任务并过一会儿取消,或者不取消
---@param doer ent|nil # 执行者,不填则生成临时预制物用来跑任务
---@param taskname string|nil # 任务名, 如果不填 `doer` , 那么这里也可以不填
---@param interval number # 周期间隔
---@param duration number|nil # 不填时则不主动取消周期任务
---@param fn function # 周期函数
---@param fn_when_time_up function|nil # duration到0时执行
---@param initialdelay number|nil # 初始延迟,填0则立刻执行,不填则以 `interval` 为延迟
---@return string taskname # 任务名
function tool:taskDoPeriodicForAWhile(doer,taskname,interval,duration,fn,fn_when_time_up,initialdelay)
    if taskname == nil then
        taskname = 'task_periodic_temp_' .. tostring(os.clock())
    end
    local temp = nil
    if doer == nil then
        doer = CreateEntity()
        temp = doer
    end
    if doer[taskname] ~= nil then doer[taskname]:Cancel() doer[taskname] = nil end
    if doer[taskname..'_cancel'] ~= nil then doer[taskname..'_cancel']:Cancel() doer[taskname..'_cancel'] = nil end
    doer[taskname] = doer:DoPeriodicTask(interval,fn,initialdelay or interval)
    if duration ~= nil then
        doer[taskname..'_cancel'] = doer:DoTaskInTime(duration,function()
            if doer and doer[taskname] ~= nil then doer[taskname]:Cancel() doer[taskname] = nil end
            if doer and doer[taskname..'_cancel'] ~= nil then doer[taskname..'_cancel']:Cancel() doer[taskname..'_cancel'] = nil end
            if fn_when_time_up ~= nil then
                fn_when_time_up()
            end
            if temp and temp:IsValid() then
                temp:Remove()
            end
            temp = nil
        end)
    end
    return taskname
end

---做倒计时任务
---@param doer ent|nil # 执行者,不填则生成临时预制物用来跑任务
---@param taskname string|nil # 任务名, 如果不填 `doer` , 那么这里也可以不填
---@param time number
---@param fn function|nil
---@param kill_task_first boolean|nil # 是否先取消之前的任务,默认取消
---@return string taskname # 任务名
function tool:taskDoInTime(doer,taskname,time,fn,kill_task_first)
    if taskname == nil then
        taskname = 'task_intime_temp_' .. tostring(os.clock())
    end
    local temp = nil
    if doer == nil then
        doer = CreateEntity()
        temp = doer
    end
    if kill_task_first == nil or kill_task_first == true then
        if doer[taskname] ~= nil then doer[taskname]:Cancel() doer[taskname] = nil end
    end
    doer[taskname] = doer:DoTaskInTime(time,function ()
        if fn ~= nil then
            fn()
        end
        if doer and doer[taskname] ~= nil then doer[taskname]:Cancel() doer[taskname] = nil end
        if temp and temp:IsValid() then
            temp:Remove()
        end
        temp = nil
    end)
    return taskname
end

---取消一个挂在实体上的任务,并置空所在的字段
---@param doer ent
---@param taskname string
function tool:taskCancel(doer,taskname)
    if doer[taskname] ~= nil then
        doer[taskname]:Cancel()
    end
    doer[taskname] = nil
end

---任务存不存在
---@param doer ent|nil
---@param taskname string
---@return boolean
---@nodiscard
function tool:taskIfExist(doer,taskname)
    return doer ~= nil and doer[taskname] ~= nil
end

---将一个实体挂到另一个母体上,并返回这个实体
---@param owner ent
---@param field string
---@param ent ent
---@param remove_before_add boolean|nil # 是否在添加前 确保该字段下没有实体
---@param setparent_or_followsymbol boolean|nil # `true`: SetParent , `false`: FollowSymbol
---@param symbol_whenfollowsymbol string|nil # 当使用`FollowSymbol`时, 跟随的通道
---@param x_followsymbol number|nil # 当使用`FollowSymbol`时才填
---@param y_followsymbol number|nil # 当使用`FollowSymbol`时才填
---@param z_followsymbol number|nil # 当使用`FollowSymbol`时才填
---@return ent
---@nodiscard
function tool:addEntRefer(owner,field,ent,remove_before_add,setparent_or_followsymbol,symbol_whenfollowsymbol,x_followsymbol,y_followsymbol,z_followsymbol)
    if remove_before_add then
        if owner[field] ~= nil and owner[field]:IsValid() then
            owner[field]:Remove()
        end
        owner[field] = nil
    end
    owner[field] = ent
    if setparent_or_followsymbol == true then
        ent.entity:SetParent(owner.entity)
    elseif setparent_or_followsymbol == false then
        ent.entity:AddFollower()
        ent.Follower:FollowSymbol(owner.GUID, symbol_whenfollowsymbol or nil,x_followsymbol or 0,y_followsymbol or 0,z_followsymbol or 0)
    end
    return ent
end

---移除实体引用
---@param owner ent
---@param field string
function tool:removeEntRefer(owner,field)
    if owner[field] ~= nil and owner[field]:IsValid() then
        owner[field]:Remove()
    end
    owner[field] = nil
end

---获取默认方向到指定方向的转角 与GetRotation略有不同
---@param x1 number # 向量起点x坐标
---@param z1 number # 向量起点z坐标
---@param x2 number # 向量终点x坐标
---@param z2 number # 向量终点z坐标
---@return number
---@nodiscard
function tool:angleBetweenVectors(x1, z1, x2, z2)
    local x0,z0 = x1 + 1,z1
    local vec1X,vec1Z = x0 - x1,z0 - z1 -- 默认方向向量
    local vec2X,vec2Z = x2 - x1,z2 - z1 -- 向量AB

    local dotProduct = vec1X * vec2X + vec1Z * vec2Z -- 点乘
    local magA,magB = math.sqrt(vec1X^2+vec1Z^2),math.sqrt(vec2X^2+vec2Z^2) -- 向量模长

    local cosTheta = dotProduct / (magA * magB) -- 余弦值
    local theta = math.deg(math.acos(cosTheta)) -- 角度
    if z2-z1>0 then theta = 360-theta end -- 旋转是固定顺时针的,所以要转的角度超过180度时,要...
    return theta
end

AddPlayerPostInit(function(inst)
    if not TheWorld.ismastersim then
        TheInput:AddKeyUpHandler(KEY_H, function()
            if inst == ThePlayer and TheFrontEnd:GetActiveScreen() and TheFrontEnd:GetActiveScreen().name == 'HUD' then
                if tool:taskIfExist(inst,'taskperiod_sync_m_pos') then
                    -- 在释放中则取消
                    tool:taskCancel(inst,'taskperiod_sync_m_pos')
                    SendModRPCToServer(GetModRPC(namespace,'lightning_off'))
                else
                    -- 没在释放技能则释放
                    SendModRPCToServer(GetModRPC(namespace,'lightning_on'))
                    tool:taskDoPeriodicForAWhile(inst,'taskperiod_sync_m_pos',.1,nil,function()
                        local x,_,z = ConsoleWorldPosition():Get()
                        SendModRPCToServer(GetModRPC(namespace,'sync_m_pos'),x,z)
                    end)
                end
            end
        end)
        return inst
    end
end)


AddModRPCHandler(namespace,'sync_m_pos',function (player, x, z)
    print(x,z)
    if player and x and z then
        player._m_x = x
        player._m_z = z
    end
end)

---@type {[1]:string, [2]:string, [3]:string, [4]:number, [5]:number, [6]:number}[] # bank build anim rotate_fix_angle scale_x scale_y
local lighting_fx = {
    {'lightning','lightning','anim',-90,.7,.6},
    {'mastupgrade_lightningrod_fx','mastupgrade_lightningrod_fx','idle',-90,1,2},
    {'lightning_rod_fx','lightning_rod_fx','idle',-90,1,2.4},
    {'moonstorm_lightningstrike','moonstorm_lightningstrike','strike',-90,.5,.6},
}

local len = #lighting_fx

AddModRPCHandler(namespace,'lightning_on',function (player)

    local fx = tool:addEntRefer(player,'fx_lightning',SpawnPrefab(blank_prefab),true,false,nil,0,0,0)
    fx.AnimState:SetOrientation(ANIM_ORIENTATION.OnGround)
    fx.AnimState:SetLayer(LAYER_BACKGROUND)
    fx:AddTag('NOBLOCK')
    fx:AddTag('FX')
    fx:AddTag('NOCLICK')
    fx.persists = false

    local rdm = lighting_fx[math.random(1,len)]
    fx.AnimState:SetBank(rdm[1])
    fx.AnimState:SetBuild(rdm[2])
    fx.AnimState:PlayAnimation(rdm[3],true)
    fx.AnimState:SetScale(rdm[5],rdm[6])

    fx:ListenForEvent('animover',function ()
        if player == nil or not player:IsValid() then
            if fx and fx:IsValid() then
                fx:Remove()
            end
            return
        end
        if fx and fx:IsValid() then
            rdm = lighting_fx[math.random(1,len)]
            fx.AnimState:SetBank(rdm[1])
            fx.AnimState:SetBuild(rdm[2])
            fx.AnimState:PlayAnimation(rdm[3],true)
            fx.AnimState:SetScale(rdm[5],rdm[6])
        end
    end)

    tool:taskCancel(player,'taskperiod_sync_lightning_pos')
    tool:taskDoPeriodicForAWhile(player,'taskperiod_sync_lightning_pos',0,nil,function ()
        if player == nil or not player:IsValid() then
            if fx and fx:IsValid() then
                fx:Remove()
            end
            return
        end
        if fx == nil or not fx:IsValid() then
            return
        end
        local x,z = player._m_x,player._m_z
        local p_x,_,p_z = player:GetPosition():Get()
        if x and z and p_x and p_z then
            local angle = tool:angleBetweenVectors(p_x,p_z,x,z)
            fx.Transform:SetRotation(angle-90)
        end
    end,nil,0)
end)

AddModRPCHandler(namespace,'lightning_off',function (player)
    tool:removeEntRefer(player,'fx_lightning')
    tool:taskCancel(player,'taskperiod_sync_lightning_pos')
end)

]]



--[[ 


{
    key = "星粉", -- 自定义名称 这个不需要关心 当作数据表的标记吧
    title_name = "STELLA_PINK", --地皮名称
    land = "LAND",
    texture = "carpet", -- 边缘纹理 没有 就用原版 牛毛地毯 carpet 棋盘 blocky 岩石 rocky
    noise_texture = "stella_pink_noise_texture", -- 地皮纹理 levels/texture 的tex名称
    -- 地皮行走声音
    runsound = "dontstarve/movement/run_marble",
    walksound = "dontstarve/movement/walk_marble",
    snowsound = "dontstarve/movement/run_ice",
    mudsound = "dontstarve/movement/run_mud",
    -- flashpoint_modifier = 1.5,                  -- 易燃系数 默认1 这个是乘法  已经弃用
    no_fire_spread = true,                            -- 阻止火焰蔓延
    flooring = false,                                 -- 标记为true则上面不能生长植物
    hard = false,                                     -- 标记为true则上面不可种植植物
    roadways = true,                                  -- 标记为true则玩家在上面可以加速，类似于卵石路
    cannotbedug = nil,                                -- 标记为true则不能挖掉
    mini_name = "map_edge",                           -- 懒得管就这里默认
    mini_noise_texture = "stella_pink_noise_texture", -- 小地图图片 地图上面显示的区块颜色
    product = "stella_pink",                          -- 掉落物的代码 配方要跟 turf_在前面
    product_anim = "stella_pink",                     -- idle状态的动画名称
    product_bank_build = "stella_pink",               -- 动画包的build和bank
    -- product_pickupsound = "vegetation_grassy",  -- 地皮被捡起来的声音 或许
},


 ]]